package com.gitlab.techschool.pcbook.service;

import com.gitlab.techschool.pcbook.pb.*;
import com.google.protobuf.ByteString;
import io.grpc.Context;
import io.grpc.Status;
import io.grpc.stub.StreamObserver;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.UUID;
import java.util.logging.Logger;

/**
 * service
 */
public class LaptopService extends LaptopServiceGrpc.LaptopServiceImplBase {

    private static final Logger LOGGER = Logger.getLogger(LaptopService.class.getName());

    private final LaptopStore laptopStore;
    private final ImageStore imageStore;

    public LaptopService(LaptopStore laptopStore, ImageStore imageStore) {
        this.laptopStore = laptopStore;
        this.imageStore = imageStore;
    }

    /**
     * 创建笔记本(unary)
     */
    @Override
    public void createLaptop(CreateLaptopRequest request, StreamObserver<CreateLaptopResponse> responseObserver) {
        Laptop laptop = request.getLaptop();
        String id = laptop.getId();
        LOGGER.info("got a crate-laptop request with ID: " + id);

        // 验证id
        UUID uuid;
        if (id.isEmpty()) {
            uuid = UUID.randomUUID();
        } else {
            try {
                uuid = UUID.fromString(id);
            } catch (IllegalArgumentException e) {
                responseObserver.onError(
                        Status.INVALID_ARGUMENT
                                .withDescription(e.getMessage())
                                .asRuntimeException()
                );
                return;
            }
        }

//        // 模拟耗时的任务，造成请求超时
//        try {
//            TimeUnit.SECONDS.sleep(6);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }

        // 处理请求超时
        if (Context.current().isCancelled()) {
            LOGGER.info("request is cancelled");
            responseObserver.onError(
                    Status.CANCELLED
                            .withDescription("request is cancelled")
                            .asRuntimeException()
            );
            return;
        }

        // 保存
        Laptop other = laptop.toBuilder().setId(uuid.toString()).build();
        try {
            laptopStore.save(other);
        } catch (AlreadyExistsException e) {
            responseObserver.onError(
                    Status.ALREADY_EXISTS
                            .withDescription(e.getMessage())
                            .asRuntimeException()
            );
            return;
        } catch (Exception e) {
            responseObserver.onError(
                    Status.INTERNAL
                            .withDescription(e.getMessage())
                            .asRuntimeException()
            );
            return;
        }

        // 响应
        CreateLaptopResponse response = CreateLaptopResponse.newBuilder()
                .setId(other.getId())
                .build();
        responseObserver.onNext(response);
        responseObserver.onCompleted();
        LOGGER.info("saved laptop with ID: " + other.getId());
    }

    /**
     * 按条件搜索笔记本(server stream)
     */
    @Override
    public void searchLaptop(SearchLaptopRequest request, StreamObserver<SearchLaptopResponse> responseObserver) {
        Filter filter = request.getFilter();
        LOGGER.info("got a search-laptop request with filter:\n " + filter);
        laptopStore.search(filter, laptop -> {
            responseObserver.onNext(SearchLaptopResponse.newBuilder()
                    .setLaptop(laptop)
                    .build());
        });
        // 告诉客户端不会有更多响应了
        responseObserver.onCompleted();
        LOGGER.info("search laptop completed");
    }

    /**
     * 上传图片(client stream)
     * client stream：方法返回一个StreamObserver对象，需要我们自己去实现一个实现类，实现里面的三个方法
     */
    @Override
    public StreamObserver<UploadImageRequest> uploadImage(StreamObserver<UploadImageResponse> responseObserver) {
        return new StreamObserver<UploadImageRequest>() {

            private String laptopId;
            private String imageType;
            private ByteArrayOutputStream imageData;
            private static final int MAX_IMAGE_SIZE = 1 << 20; // 1 megabyte

            @Override
            public void onNext(UploadImageRequest value) {
                // 判断请求参数
                if (UploadImageRequest.DataCase.INFO.equals(value.getDataCase())) {
                    // 图片基本信息
                    ImageInfo info = value.getInfo();
                    LOGGER.info("receive image info\n:" + info);
                    laptopId = info.getLaptopId();
                    imageType = info.getImageType();
                    imageData = new ByteArrayOutputStream();

                    // 检查笔记本id是否存在
                    Laptop laptop = laptopStore.find(laptopId);
                    if (laptop == null) {
                        LOGGER.info("laptop not found, id: " + laptopId);
                        responseObserver.onError(Status.NOT_FOUND
                                .asRuntimeException());
                    }

                    return;
                }

                // 图片字节
                ByteString chunkData = value.getChunkData();
                LOGGER.info("receive data chunk with size: " + chunkData.size());

                // 检查是否超过设定的图片最大size
                int size = chunkData.size() + imageData.size();
                if (size > MAX_IMAGE_SIZE) {
                    LOGGER.info("image too large: " + size);
                    responseObserver.onError(Status.INVALID_ARGUMENT
                            .withDescription("image is too large: " + size)
                            .asRuntimeException());
                }


                // 判断是否发送过图片info
                if (imageData == null) {
                    LOGGER.info("image info wasn't send before");
                    responseObserver.onError(Status.INVALID_ARGUMENT
                            .withDescription("image info wasn't send before")
                            .asRuntimeException());
                    return;
                }

                // 将图片字节写入imageData
                try {
                    chunkData.writeTo(imageData);
                } catch (IOException e) {
                    LOGGER.info("cannot write chunk data: " + e.getMessage());
                    responseObserver.onError(Status.INTERNAL
                            .withDescription("cannot write chunk data: " + e.getMessage())
                            .asRuntimeException());
                }
            }

            @Override
            public void onError(Throwable t) {
                LOGGER.warning(t.getMessage());
            }

            @Override
            public void onCompleted() {
                String imageId = null;
                int imageSize = imageData.size();

                try {
                    // 保存到磁盘
                    imageId = imageStore.save(laptopId, imageType, imageData);
                } catch (IOException e) {
                    LOGGER.info("cannot save image to the store: " + e.getMessage());
                    responseObserver.onError(Status.INTERNAL
                            .withDescription("cannot save image to the store")
                            .asRuntimeException());
                }

                UploadImageResponse response = UploadImageResponse.newBuilder()
                        .setId(imageId)
                        .setSize(imageSize)
                        .build();
                responseObserver.onNext(response);
                responseObserver.onCompleted();
            }
        };
    }
}
